home *** CD-ROM | disk | FTP | other *** search
- ///////////////////////////////////////////////////////////////////////////////
- //
- // File: DrawingFunctions.cp
- //
- // Project: MacHack 2000 - Vertigo!
- // Authors: Darrin Cardani, Drew Thaler, Ed Wynne
- //
- // Date: 06/23/2000 (written entirely during the conference!)
- //
- ///////////////////////////////////////////////////////////////////////////////
-
- #define DISABLE_LOCAL_CALLTRACE 0
- #define DISABLE_LOCAL_DEBUG 0
- #include "DebugUtils.h"
-
- #include "DrawingFunctions.h"
- #include "DrawingUtils.h"
-
- #include <Quickdraw.h>
- #include <QDOffscreen.h>
-
-
- // A simple enum that defines the maximum "fuzz" space around a region.
- enum { kRegionBorder = 24 };
-
- #define SWAPSHORTS(x,y) do { register SInt16 swap = x; x = y; y = swap; } while(false)
- #define FINDNEXTSCANLINE() \
- do { \
- if (delta < 0) \
- { \
- parse = scanlineparser - 2; \
- \
- while (*parse != 0x7FFF) \
- parse--; \
- \
- if (*parse == 0x7FFF) parse++; \
- } \
- \
- if (parse == rgnEnd) doneParsing = true; \
- \
- scanlineparser = parse; \
- } while (false)
-
-
- #define kMaxInversionPoints 4096
- SInt16 gInversionStack[kMaxInversionPoints];
- UInt16 gInversionStackPtr;
-
- typedef struct MyRegion MyRegion;
- struct MyRegion {
- unsigned short rgnSize; /*size in bytes*/
- Rect rgnBBox; /*enclosing rectangle*/
- SInt16 rgnData[1];
- };
-
-
- void BlurImageHorz (GWorldPtr gwpInImage, GWorldPtr gwpOutImage, const Rect* prInArea);
- void BlurImageVert (GWorldPtr gwpInImage, GWorldPtr gwpOutImage, SInt32 iInOffset, const Rect* prInArea);
- void OffsetImage (GWorldPtr gwpInImage, SInt32 iInOffset, GWorldPtr gwpOutImage);
-
-
-
- // ------------------------------------------------------------
- // DrawDropShadows
- // ------------------------------------------------------------
- // Renders window drop shadows onscreen, into a scratch GWorld.
-
- void RenderDropShadows(const GWorldPtr screenGWorld,
- GWorldPtr dropShadowWorld,
- RgnHandle maskRgn,
- RegionList &windowRegions)
- {
- // How much are the shadows offset?
- enum { kShadowOffset = 16 };
-
- // Do we want to cheat for rectangles? We get faster, but the output's not as nice...
- enum { cheatOnRectangles = false };
-
- // Fuzziness value at which we judge a region to be approximately rectangular
- // (a common case which we optimize). Set to 0 if you want exact drop shadows.
- enum { kApproximatelyARectangleFuzziness = 2 };
-
- // Colors for the shadows. The larger the values get the darker the shadow will be.
- // This color table starts from the edge (where the shadow is lightest) and goes
- // in to the middle (where the shadow is darkest).
- static const RGBColor shadowColors[kShadowOffset] = {
- {0x0800,0x0800,0x0800},
- {0x1000,0x1000,0x1000}, {0x1800,0x1800,0x1800},
- {0x2000,0x2000,0x2000}, {0x2800,0x2800,0x2800},
- {0x3000,0x3000,0x3000}, {0x3800,0x3800,0x3800},
- {0x4000,0x4000,0x4000}, {0x4800,0x4800,0x4800},
- {0x5000,0x5000,0x5000}, {0x5800,0x5800,0x5800},
- {0x6000,0x6000,0x6000}, {0x6800,0x6800,0x6800},
- {0x7000,0x7000,0x7000}, {0x7800,0x7800,0x7800},
- {0x8000,0x8000,0x8000}};
-
- // First thing to do: copy the entire screen over to the drop shadow world.
- // (Clipped to the mask that we care about, of course).
- CopyGWorldToGWorld(screenGWorld,dropShadowWorld,maskRgn);
-
- // Next, iterate through the window regions (which we keep in back-to-front
- // order) and draw their drop shadows into the shadow world.
- StSetGWorld setPort(dropShadowWorld);
- StRegion shadowRgn, tempRgn, clipRgn, oldClip;
- Rect boundsRect, tempRect;
- for (int i=0; i<windowRegions.GetCount(); ++i)
- {
- // Calculate the shadow region for the window, which is
- // simply the region offset by a certain amount.
- shadowRgn = windowRegions[i];
- OffsetRgn(shadowRgn,kShadowOffset,kShadowOffset);
-
- // Calculate the clipping region for this window's shadow by
- // subtracting out all the windows including and above the current one.
- clipRgn = shadowRgn;
- if (maskRgn != NULL) clipRgn &= maskRgn;
- for (int j=i; j<windowRegions.GetCount(); ++j)
- clipRgn -= windowRegions[j];
- if (clipRgn.IsEmpty())
- continue;
-
- StClipRgnState setClip(clipRgn);
-
- // First of all - is the window close to rectangular? We can
- // totally cheat if it is.
- boundsRect = tempRect = windowRegions[i][0]->rgnBBox;
- tempRect.top += kApproximatelyARectangleFuzziness; tempRect.left += kApproximatelyARectangleFuzziness;
- tempRect.right -= kApproximatelyARectangleFuzziness; tempRect.bottom -= kApproximatelyARectangleFuzziness;
- tempRgn = tempRect;
- tempRgn &= windowRegions[i];
- if ((cheatOnRectangles) && (tempRgn == tempRect))
- {
- // Yes, it's rectangular (the common case which we
- // really want to optimize). This could
- // be way more optimal (that's left as an exercise to
- // the reader) ... but it's at least better than
- // a bunch of FrameRgns.
- register short bottomy = boundsRect.bottom + kShadowOffset - 1;
- register short topy = boundsRect.top + kShadowOffset;
- register short leftx = boundsRect.left + kShadowOffset;
- register short rightx = boundsRect.right + kShadowOffset - 1;
-
- PenMode(subPin);
- for (int i=0; i<kShadowOffset; ++i)
- {
- RGBForeColor(&shadowColors[i]);
- MoveTo(leftx,boundsRect.bottom);
- LineTo(leftx,bottomy);
- Move(1,0);
- LineTo(rightx,bottomy);
- Move(0,-1);
- LineTo(rightx,topy);
- Move(-1,0);
- LineTo(boundsRect.right,topy);
-
- leftx += 1;
- topy += 1;
- bottomy -= 1;
- rightx -= 1;
- }
- PenMode(normal);
- }
- else
- {
- // Calculate a nonstandard drop shadow region. We offset the
- // window by 8 pixels down and right, then need to clip
- // out all window regions above and including the current one.
- shadowRgn = windowRegions[i];
- OffsetRgn(shadowRgn,kShadowOffset,kShadowOffset);
-
- // Round off the edges. This makes rectangular window shadows
- // softer and more realistic.
- OpenRgn();
- FrameRoundRect(&shadowRgn.Bounds(),kShadowOffset,kShadowOffset);
- CloseRgn(tempRgn);
- shadowRgn &= tempRgn;
-
- // Okay, we've got the drop shadow region. We're already
- // clipped, so just draw it.
- PenMode(subPin);
- for (int i=0; i<kShadowOffset; ++i)
- {
- RGBForeColor(&shadowColors[i]);
- FrameRgn(shadowRgn);
- InsetRgn(shadowRgn,1,1);
- }
- PenMode(normal);
- }
- }
- }
-
-
-
- // ------------------------------------------------------------
- // CopyRegionForRedShifting
- // ------------------------------------------------------------
-
- void CopyRegionForRedShifting(const GWorldPtr dropShadowWorld,
- GWorldPtr preShiftWorld,
- RgnHandle rgn)
- {
- // Create a region that's a little bigger than the source.
- StRegion bigger(rgn);
- InsetRgn(bigger,-kRegionBorder,-kRegionBorder);
-
- // Subtract out the original to get a border area.
- StRegion border(bigger);
- border -= rgn;
-
- // Erase the border to the shadow color.
- StSetGWorld setPort(preShiftWorld);
- RGBColor shadowColor = {0x8000,0x8000,0x8000};
- RGBBackColor(&shadowColor);
- EraseRgn(border);
-
- // Grab the left edge of the border - we're going to make that white
- // because otherwise it looks like the shadow extends to the left.
- StRegion leftBorder(border);
- OffsetRgn(leftBorder,kRegionBorder,0);
- leftBorder -= border;
- BackColor(whiteColor);
- EraseRgn(leftBorder);
-
- // Now copy in the source data.
- CopyGWorldToGWorld(dropShadowWorld,preShiftWorld,rgn);
- }
-
-
-
- // ------------------------------------------------------------
- // RedShiftAndBlur
- // ------------------------------------------------------------
-
- void RedShiftAndBlur(const GWorldPtr preShiftWorld,
- GWorldPtr scratchWorld,
- GWorldPtr postShiftWorld,
- RgnHandle blitRgn,
- int depth)
- {
- dprintf("RedShiftAndBlur - depth = %d\n", depth);
- /*
- {
- StRegion bigger(blitRgn);
- InsetRgn(bigger,-depth,-1);
- bigger &= postShiftWorld->portRect;
- CopyRgn(bigger,blitRgn);
-
- StSetGWorld setPort(postShiftWorld);
- CopyGWorldToGWorld(preShiftWorld,postShiftWorld,blitRgn);
-
- ForeColor(redColor);
- FrameRgn(blitRgn);
- return;
- }
- */
- /*
- // Erase the scratch world (plus a bit) to the shadow color.
- {
- StSetGWorld setPort(scratchWorld);
- StRegion bigger(blitRgn);
-
- InsetRgn(bigger,-kRegionBorder,-kRegionBorder);
-
- RGBColor shadowColor = {0x8000,0x8000,0x8000};
- RGBBackColor(&shadowColor);
- EraseRgn(bigger);
- }
- */
- // Now, when copying back into the output, we need to copy a bigger
- // region than we started with, because the red-shift expands
- // the image (by the depth amount, in fact). If we were
- // really cool and not so rushed, we'd intelligently overlay
- // this onto the background... for now we just copy over.
- StRegion bigger(blitRgn);
- InsetRgn(bigger,-depth,-1);
- bigger &= postShiftWorld->portRect;
- CopyRgn(bigger,blitRgn);
-
- // Break the red-shift up into rectangle sized chunks.
- Rect &srcRect = (**blitRgn).rgnBBox;
- Rect &dstRect = (**blitRgn).rgnBBox;
-
- SInt16 *rgnData, *rgnEnd;
- SInt16 *scanlineparser, *parse, delta;
- SInt16 y,x1,x2,topScanLine,botScanLine;
- Boolean doneParsing = false;
- MyRegion* maskPtr;
- Rect blitDst;
-
- SInt32 i,j;
-
- // *** hack to fix rectangular regions -- it seems that anything with rgnSize == 0x0A
- // is either a rect rgn or an empty rgn... the catch is, the actual region data is junk
- // and you're only supposed to use the bounding box. Catch that case here.
-
- if ( (**blitRgn).rgnSize == 10 )
- {
- blitDst = (**blitRgn).rgnBBox;
-
- // don't blit if it's an empty region
- if ( blitDst.right > blitDst.left && blitDst.bottom > blitDst.top )
- RedShiftAndBlurRect(preShiftWorld,scratchWorld,postShiftWorld,&blitDst,depth);
-
- return;
- }
-
- // Set up the parsing variables
-
- gInversionStackPtr = 0;
- maskPtr = (MyRegion*) (*blitRgn); // note -- we're not locking the handle,
- // because we never call the toolbox again.
-
- rgnData = (SInt16*) maskPtr->rgnData;
- rgnEnd = (SInt16*) ((UInt32) maskPtr + maskPtr->rgnSize) - 1;
- scanlineparser = parse = rgnData;
- delta = +1;
-
- // Now we just need to parse the region into rect-sized chunks and handle
- // them individually.
-
- while (!doneParsing)
- {
-
- // Start walking the region data. scanlineparser points to the
- // beginning of the current scan line, and parse is used as a walk pointer
- // to extract data.
-
- y = *(parse++);
- x1 = *(parse++);
-
- while (x1 != 0x7FFF)
- {
- x2 = *(parse++);
-
- // Need to insertion-sort x1 and x2 into the inversion list. We're
- // guaranteed that x1 < x2, which helps a bit, but it gets more complex
- // because if we find a point that's EQUAL to x1 or x2, the two cancel
- // each other out and both have to be removed. There's probably a way
- // to do this with a single loop, but for now we'll just loop twice.
-
- // step 1 -- insertion sort
-
- for (i=0; i<gInversionStackPtr; ++i)
- {
- // bubble the values up so x1 and x2 always contain the largest values so far
-
- if (gInversionStack[i] > x1)
- SWAPSHORTS( x1, gInversionStack[i] );
-
- if (x1 > x2)
- SWAPSHORTS( x1, x2 );
-
- }
- gInversionStack[gInversionStackPtr++] = x1;
- gInversionStack[gInversionStackPtr++] = x2;
-
- // step 2 -- remove redundancies
-
- for (i=0; i<gInversionStackPtr-1; ++i)
- {
- if ( gInversionStack[i] == gInversionStack[i+1] )
- {
- for (j=i+2;j<gInversionStackPtr;++j)
- gInversionStack[j-2] = gInversionStack[j];
-
- gInversionStackPtr -= 2;
- i--;
- }
- }
-
- x1 = *(parse++);
- }
-
- // Now we've got our inversion list for the current scan line,
- // describing what needs to be blitted. Get the next scan line's Y coord
- // and build a bunch of rects for blitting.
-
- topScanLine = y;
- FINDNEXTSCANLINE();
- botScanLine = (*parse);
-
- // skip blit step if we have nothing to blit
-
- if (gInversionStackPtr == 0)
- continue;
-
- if (botScanLine == 0x7FFF) // we *shouldn't* ever have this happen, because
- break; // gInversionStackPtr should be 0 and we should break
- // above with doneParsing = true, but just in case...
-
- if (topScanLine > botScanLine)
- SWAPSHORTS( topScanLine, botScanLine );
-
- blitDst.top = topScanLine;
- blitDst.bottom = botScanLine;
-
- // blit rects left to right
- for ( i=0; i<gInversionStackPtr-1; i+=2 )
- {
- blitDst.left = gInversionStack[i];
- blitDst.right = gInversionStack[i+1];
-
- RedShiftAndBlurRect(preShiftWorld,scratchWorld,postShiftWorld,&blitDst,depth);
- }
- }
- }
-
-
- // ------------------------------------------------------------
- // RedShiftAndBlurRect
- // ------------------------------------------------------------
-
- void RedShiftAndBlurRect(const GWorldPtr preShiftWorld,
- GWorldPtr scratchWorld,
- GWorldPtr postShiftWorld,
- const Rect *inRect,
- int depth)
- {
- BlurImageHorz(preShiftWorld,scratchWorld,inRect);
- BlurImageVert(scratchWorld,postShiftWorld,depth,inRect);
- }
-
-
- // ------------------------------------------------------------
- // CopyIntoComposite
- // ------------------------------------------------------------
-
- void CopyIntoComposite(const GWorldPtr srcWorld,
- GWorldPtr compositeWorld,
- RgnHandle rgn)
- {
- CopyGWorldToGWorld(srcWorld,compositeWorld,rgn);
- }
-
-
-
- #pragma mark -
-
-
-
- /*
- Function: BlurImageHorz
-
- Inputs: gwpInImage - the image to be blurred
-
- Outputs: gwpOutImage - the blurred image
-
- Purpose: Performs a horizontal blur on the image to simulate
- a cheap depth of field. You should call BlurImageVert after calling this
-
- */
-
- void BlurImageHorz (GWorldPtr gwpInImage, GWorldPtr gwpOutImage, const Rect* prInArea)
- {
- PixMapHandle pmhInImage = GetGWorldPixMap (gwpInImage);
- UInt8 *pSrc = NULL, *pSrc2 = NULL, *pSrc3 = NULL;
- SInt32 iSrcRowBytes = 0;
- SInt32 iSrcBump = 0;
-
- PixMapHandle pmhOutImage = GetGWorldPixMap (gwpOutImage);
- UInt8* pDst = NULL;
- SInt32 iDstRowBytes = 0;
- SInt32 iDstBump = 0;
-
- SInt32 height = 0, width = 0;
- SInt32 iWidth = 0;
-
- iWidth = prInArea->right - prInArea->left;
- height = prInArea->bottom - prInArea->top;
-
- //LockPixels (pmhInImage);
- pSrc = (UInt8*)GetPixBaseAddr (pmhInImage);
- iSrcRowBytes = GetPixRowBytes (pmhInImage);
- iSrcBump = iSrcRowBytes - (iWidth * 4);
- pSrc = pSrc + (prInArea->top * iSrcRowBytes) + (prInArea->left * 4);
-
- //LockPixels (pmhOutImage);
- pDst = (UInt8*)GetPixBaseAddr (pmhOutImage);
- iDstRowBytes = GetPixRowBytes (pmhOutImage);
- iDstBump = iDstRowBytes - (iWidth * 4);
- pDst = pDst + (prInArea->top * iDstRowBytes) + (prInArea->left * 4);
-
- while (height-- > 0) {
-
- width = iWidth - 2;
-
- pSrc++;
- pDst++;
- *pDst++ = *pSrc++;
- *pDst++ = *pSrc++;
- *pDst++ = *pSrc++;
- pSrc2 = pSrc + 4;
- pSrc3 = pSrc2 + 4;
-
- // Skip Alpha Channels
- pSrc++;
- pSrc2++;
- pSrc3++;
- pDst++;
-
- while (width-- > 0) {
-
- // Handle red channel
- *pDst = (*pSrc + (2 * *pSrc2) + *pSrc3) >> 2;
- pSrc++;
- pSrc2++;
- pSrc3++;
- pDst++;
-
- // Handle green channel
- *pDst = (*pSrc + (2 * *pSrc2) + *pSrc3) >> 2;
- pSrc++;
- pSrc2++;
- pSrc3++;
- pDst++;
-
- // Handle blue channel
- *pDst = (*pSrc + (2 * *pSrc2) + *pSrc3) >> 2;
- pSrc+=2;
- pSrc2+=2;
- pSrc3+=2;
- pDst+=2;
- }
-
- *pDst++ = *pSrc++;
- *pDst++ = *pSrc++;
- *pDst++ = *pSrc++;
-
- pSrc += iSrcBump;
- pDst += iDstBump;
- }
- }
-
-
- /*
- Function: BlurImageVert
-
- Inputs: gwpInImage - the image to be blurred
-
- Outputs: gwpOutImage - the blurred image
-
- Purpose: Performs a vertical blur on the image to simulate
- a cheap depth of field. You should call BlurImageHorz before calling this
-
- */
-
- void BlurImageVert (GWorldPtr gwpInImage, GWorldPtr gwpOutImage, SInt32 iInOffset, const Rect* prInArea)
- {
- PixMapHandle pmhInImage = GetGWorldPixMap (gwpInImage);
- UInt8 *pSrc = NULL, *pSrc2 = NULL, *pSrc3 = NULL;
- SInt32 iSrcRowBytes = 0;
- SInt32 iSrcBump = 0;
-
- PixMapHandle pmhOutImage = GetGWorldPixMap (gwpOutImage);
- UInt8* pDst = NULL;
- SInt32 iDstRowBytes = 0;
- SInt32 iDstBump = 0;
-
- SInt32 height = 0, width = 0;
- SInt32 iWidth;
-
- UInt32 *pSrc32 = NULL, *pDst32 = NULL;
- SInt32 iOffset = iInOffset * 4;
- SInt32 absiInOffset = (iInOffset < 0) ? -iInOffset:iInOffset;
- SInt32 iTemp = 0;
-
- iWidth = prInArea->right - prInArea->left;
- height = prInArea->bottom - prInArea->top - 2;
-
- //LockPixels (pmhInImage);
- pSrc = (UInt8*)GetPixBaseAddr (pmhInImage);
- iSrcRowBytes = GetPixRowBytes (pmhInImage);
- iSrcBump = iSrcRowBytes - (iWidth * 4) - 1;
- pSrc = pSrc + (prInArea->top * iSrcRowBytes) + (prInArea->left * 4);
- pSrc2 = pSrc + iSrcRowBytes;
- pSrc3 = pSrc2 + iSrcRowBytes;
-
- //LockPixels (pmhOutImage);
- pDst = (UInt8*)GetPixBaseAddr (pmhOutImage);
- iDstRowBytes = GetPixRowBytes (pmhOutImage);
- iDstBump = iDstRowBytes - (iWidth * 4) - 1;
- pDst = pDst + (prInArea->top * iDstRowBytes) + (prInArea->left * 4);
-
- // Handle the first row
- width = iWidth;
- while (width-- > 0) {
- *pSrc++;
- *pDst++;
- *pDst = *(pSrc + iOffset);
- pSrc++;
- pDst++;
-
- *pDst = *(pSrc - iOffset);
- pSrc++;
- pDst++;
-
- *pDst = *(pSrc - iOffset);
- pSrc++;
- pDst++;
- }
- pDst += iDstBump + 1;
- pSrc += iSrcBump + 1;
- pSrc2 += iSrcRowBytes;
- pSrc3 += iSrcRowBytes;
-
- // Do the rest of the image (except the last row)
- while (height-- > 0) {
-
- width = iWidth + 1;
-
- // Skip Alpha channels
- pSrc++;
- pSrc2++;
- pSrc3++;
- pDst++;
-
- iTemp = absiInOffset;
- while (iTemp-- > 0) {
- width--;
- // Handle red channel
- *pDst = (*(pSrc + iOffset) + (*(pSrc2 + iOffset) * 2) + *(pSrc3 + iOffset)) >> 2;
- pSrc += 4;
- pSrc2 += 4;
- pSrc3 += 4;
- pDst++;
-
- // Handle green channel
- *pDst = 0;
- pDst++;
-
- // Handle blue channel
- *pDst = 0;
-
- pDst += 2;
- }
-
- while (width-- > absiInOffset) {
-
- // Handle red channel
- *pDst = (*(pSrc + iOffset) + (*(pSrc2 + iOffset) * 2) + *(pSrc3 + iOffset)) >> 2;
- pSrc++;
- pSrc2++;
- pSrc3++;
- pDst++;
-
- // Handle green channel
- *pDst = (*(pSrc - iOffset) + (*(pSrc2 - iOffset) * 2) + *(pSrc3 - iOffset)) >> 2;
- pSrc++;
- pSrc2++;
- pSrc3++;
- pDst++;
-
- // Handle blue channel
- *pDst = (*(pSrc - iOffset) + (*(pSrc2 - iOffset) * 2) + *(pSrc3 - iOffset)) >> 2;
- pSrc+=2;
- pSrc2+=2;
- pSrc3+=2;
- pDst+=2;
- }
-
- while (width-- > 0) {
- // Handle red channel
- *pDst = 0;
- pSrc++;
- pSrc2++;
- pSrc3++;
- pDst++;
-
- // Handle green channel
- *pDst = (*(pSrc - iOffset) + (*(pSrc2 - iOffset) * 2) + *(pSrc3 - iOffset)) >> 2;
- pSrc++;
- pSrc2++;
- pSrc3++;
- pDst++;
-
- // Handle blue channel
- *pDst = (*(pSrc - iOffset) + (*(pSrc2 - iOffset) * 2) + *(pSrc3 - iOffset)) >> 2;
- pSrc+=2;
- pSrc2+=2;
- pSrc3+=2;
- pDst+=2;
- }
-
- pSrc += iSrcBump;
- pSrc2 += iSrcBump;
- pSrc3 += iSrcBump;
- pDst += iDstBump;
- }
-
- // Handle the last row
- width = iWidth;
- while (width-- > 0) {
- *pSrc++;
- *pDst++;
- *pDst = *(pSrc + iOffset);
- pSrc++;
- pDst++;
-
- *pDst = *(pSrc - iOffset);
- pSrc++;
- pDst++;
-
- *pDst = *(pSrc - iOffset);
- pSrc++;
- pDst++;
- }
- }
-
-
-
-